package gu.rpc.thrift;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.google.common.collect.Lists;

import com.facebook.swift.codec.metadata.ThriftCatalog;
import com.facebook.swift.codec.metadata.ThriftFieldMetadata;
import com.facebook.swift.codec.metadata.ThriftType;
import com.facebook.swift.service.metadata.ThriftMethodMetadata;
import com.facebook.swift.service.metadata.ThriftServiceMetadata;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;

/**
 * 解析swift service类型,返回 {@link ThriftServiceMetadata}实例
 * 
 * @author guyadong
 *
 */
public class SwiftServiceParser {
	/** 
	 * 当前线程解析的 swift service类型
	 * @see #parse(String, ClassLoader)
	 */
	private static final ThreadLocal<Class<?>> serviceClass = new ThreadLocal<Class<?>>();
	public static Class<?> getServiceClass(){
		return serviceClass.get();
	}
	/**
	 * @see ThriftServiceMetadata#ThriftServiceMetadata(Class, ThriftCatalog)
	 * @param swiftServiceType
	 * @return
	 */
	public static ThriftServiceMetadata parse(Class<?> swiftServiceType) {
		checkNotNull(swiftServiceType, "swiftServiceType is null");
		return new ThriftServiceMetadata(swiftServiceType, new ThriftCatalog());
	}
	/**
	 * @see #parse(String, ClassLoader)
	 * @param thriftServiceClassName
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static ThriftServiceMetadata parse(String thriftServiceClassName) throws ClassNotFoundException {
		return parse(thriftServiceClassName, (ClassLoader)null);
	}

	/**
	 * 用指定的{@link ClassLoader}加载swift service类{@code thriftServiceClassName},加载成功保存到 {@link #serviceClass}
	 * @param thriftServiceClassName
	 * @param classLoader 为null时使用当前类的 class loader
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static ThriftServiceMetadata parse(String thriftServiceClassName, ClassLoader classLoader)
			throws ClassNotFoundException {
		if (Strings.isNullOrEmpty(thriftServiceClassName))
			throw new IllegalArgumentException("thriftServiceClassName is null or empty");
		Class<?> swiftServiceType =null == classLoader
				? Class.forName(thriftServiceClassName)
				: Class.forName(thriftServiceClassName, true, classLoader);
		serviceClass.set(swiftServiceType);
		return parse(swiftServiceType);
	}
	/**
	 * @see #parse(String, boolean, String[], String[])
	 * @param thriftServiceClassName
	 * @param classpath
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static ThriftServiceMetadata parse(String thriftServiceClassName, String... classpath)
			throws ClassNotFoundException {
		return parse(thriftServiceClassName, false,null,classpath);
	}
	/**
	 * @see #parse(String, boolean, String[], String[])
	 * @param thriftServiceClassName
	 * @param classpath
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static ThriftServiceMetadata parse(String thriftServiceClassName, Collection<String> classpath)
			throws ClassNotFoundException {
		return parse(thriftServiceClassName, false,null,classpath);
	}
	/**
	 * @see #parse(String, boolean, String[], String[])
	 * @param thriftServiceClassName
	 * @param recursive
	 * @param path
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static ThriftServiceMetadata parse(String thriftServiceClassName, boolean recursive, String... path)
			throws ClassNotFoundException {
		return parse(thriftServiceClassName, recursive, path,null);
	}
	/**
	 * @see #parse(String, boolean, String[], String[])
	 * @param thriftServiceClassName
	 * @param recursive
	 * @param path
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static ThriftServiceMetadata parse(String thriftServiceClassName, boolean recursive, Collection<String> path)
			throws ClassNotFoundException {
		return parse(thriftServiceClassName, recursive, path,null);
	}
	/**
	 * 加载{@code thriftServiceClassName}指定的类<br>
	 * {@code recursive,libdirs,classpath}为所需要的{@link URLClassLoader}的参数,参见 {@link ClassLoaderUtils#makeURLClassLoader(ClassLoader, boolean, String[], String[])}
	 * @param thriftServiceClassName
	 * @param recursive
	 * @param libdirs
	 * @param classpath
	 * @return
	 * @throws ClassNotFoundException
	 * @see Class#forName(String, boolean, ClassLoader)
	 * @see #parse(String, ClassLoader)
	 */
	public static ThriftServiceMetadata parse(String thriftServiceClassName, boolean recursive, String[] libdirs,
			String[] classpath) throws ClassNotFoundException {
		return parse(thriftServiceClassName, 
				ClassLoaderUtils.makeURLClassLoader(ThriftServiceMetadata.class.getClassLoader(), recursive, libdirs,
				classpath));
	}
	/**
	 * @see #parse(String, boolean, String[], String[])
	 * @param thriftServiceClassName
	 * @param recursive
	 * @param libdirs
	 * @param classpath
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static ThriftServiceMetadata parse(String thriftServiceClassName, boolean recursive, Collection<String> libdirs,
			Collection<String> classpath) throws ClassNotFoundException {
		return parse(thriftServiceClassName,
				recursive,
				null == libdirs ? null : libdirs.toArray(new String[0]),
				null == classpath ? null : classpath.toArray(new String[0]));
	}
	/**
	 * 输出解析的{@link ThriftServiceMetadata}的基本信息
	 * @param metadata
	 * @param stream
	 */
	public static final void output(ThriftServiceMetadata metadata, PrintStream stream) {
		int mcount = 0;
		if (null == stream)
			stream = System.out;
		stream.println(metadata.getName());
		for (ThriftMethodMetadata method : metadata.getDeclaredMethods().values()) {
			stream.printf("%d name: %s ", mcount++, method.getName());
			if (!method.getMethod().getName().equals(method.getName()))
				stream.printf("original name: %s ", method.getMethod().getName());
			stream.println();
			int pcount = 0;
			for (ThriftFieldMetadata parameter : method.getParameters()) {
				stream.printf("\tparam %d: %s %s %s\n", pcount++, parameter.getRequiredness(),parameter.getName(),
						parameter.getThriftType().getJavaType());
			}
			stream.printf("\treturn:%s\n",method.getReturnType().getJavaType());
			stream.printf("\texceptions:\n%s\n",Joiner.on("\n").join(Collections2.transform(method.getExceptions().entrySet(), new Function<Entry<Short, ThriftType>,String>(){
				@Override
				public String apply(Entry<Short, ThriftType> input) {
					StringBuffer buffer =new StringBuffer();
					return buffer.append("\t\t")
							.append("id:")
							.append(input.getKey())
							.append(":")
							.append(input.getValue().getJavaType())
							.toString();
				}})));
			
		}
	}
	/**
	 * 输出解析的{@link ThriftServiceMetadata}的基本信息到String
	 * @see #output(ThriftServiceMetadata, PrintStream) 
	 * @param metadata
	 * @return
	 */
	public static final String output(ThriftServiceMetadata metadata){
		ByteArrayOutputStream out = new ByteArrayOutputStream ();
		output(metadata,new PrintStream(out));
		return out.toString();
	}
	/**
	 *  {@code Map<String, Type>}形式返回指定方法的参数名列表
	 * @param method
	 * @return
	 */
	public static final LinkedHashMap<String, Type> signtureOfMethod(ThriftMethodMetadata method){
		checkNotNull(method,"method is null");
		LinkedHashMap<String, Type> map = Maps.<String,Type>newLinkedHashMap();
		for(ThriftFieldMetadata param:method.getParameters()){
			map.put(param.getName(), param.getThriftType().getJavaType());
		}		
		return map;
	}
	/**
	 * 
	 * 返回服务抛出的所有异常类型
	 * @param metadata
	 * @return
	 */
	public static final List<ThriftType> allThrowsOf(ThriftServiceMetadata metadata){
		final LinkedHashSet<ThriftType> excpetions = new LinkedHashSet<ThriftType>();
		for (ThriftMethodMetadata method : metadata.getDeclaredMethods().values()) {
			Collections2.filter(method.getExceptions().values(), new Predicate<ThriftType>(){
				@Override
				public boolean apply(ThriftType input) {
					excpetions.add(input);
					return false;
				}});
		}
		return ImmutableList.copyOf(excpetions);
	}
	/** 服务端运行时异常类名 simple name */
	private static final ThreadLocal<String> runtimeErrorName = new ThreadLocal<String>();
	public static final void setRuntimeErrorName(String runtimeError){
		runtimeErrorName.set(runtimeError);
	}
	public static final boolean throwRuntimeError(ThriftMethodMetadata method){
		if(null == method)return false;
		return !Collections2.filter(method.getExceptions().values(), new Predicate<ThriftType>(){
			@Override
			public boolean apply(ThriftType input) {
				Type javaType = input.getJavaType();
				if(javaType instanceof Class<?>)
					return ((Class<?>)javaType).getSimpleName().equals(runtimeErrorName.get());
				return false;
			}}).isEmpty();
	}
	/**
	 * 返回所有需要抛出的异常类型,不含 {@link #runtimeErrorName}
	 * @param method 服务接口方法
	 * @param isAsync 是否是生成异步服务接口
	 * @return
	 */
	public static List<ThriftType> throwsOf(ThriftMethodMetadata method,Boolean isAsync){
		if(Boolean.TRUE == isAsync || null == method )
			return ImmutableList.of();
		return  ImmutableList.copyOf(Collections2.filter(method.getExceptions().values(), new Predicate<ThriftType>(){
			@Override
			public boolean apply(ThriftType input) {
				Type javaType = input.getJavaType();
				if(javaType instanceof Class<?>)
					return !((Class<?>)javaType).getSimpleName().equals(runtimeErrorName.get());
				return false;
			}}));
	}
	/**
	 * 生成 'throws EXP1,EXP2....'异常抛出语句,不含 {@link #runtimeErrorName}
	 * @param method
	 * @param isAsync
	 * @param pkg 
	 * @return
	 */
	public static String defineThrows(ThriftMethodMetadata method,Boolean isAsync,final String pkg){
		List<ThriftType> types = throwsOf(method, isAsync);
		return types.isEmpty() 
				? ""
				: "throws "+Joiner.on(",").join(Collections2.transform(types, new Function<ThriftType,String>(){
			@Override
			public String apply(ThriftType input) {
				if(input.getJavaType() instanceof Class<?>)
					return Strings.isNullOrEmpty(pkg)
							? ((Class<?>)input.getJavaType()).getSimpleName()
							: new StringBuffer()
								.append(pkg)
								.append(".")
								.append(((Class<?>)input.getJavaType()).getSimpleName())
								.toString();
				return "/*the exceptions type is not Class<?>*/";
			}}));
	}
	/**
	 * {@code thriftMethods}按{@code indexes}指定的索引顺序排序
	 * @param metadata
	 * @param indexes
	 * @return
	 */
	public static List<ThriftMethodMetadata> sortByDefined(ThriftServiceMetadata metadata, 
			final Map<Method, Integer>indexes){
		checkArgument(null != metadata && null != indexes, 
				"'metadata' and 'method' must not be null");
		Collection<ThriftMethodMetadata> thriftMethods = metadata.getMethods().values();
		checkArgument(thriftMethods.size() == indexes.size(),
				"mismatch size of 'thriftMethods' and 'method' ");
		return Ordering.from(new Comparator<ThriftMethodMetadata>(){
			@Override
			public int compare(final ThriftMethodMetadata o1, final ThriftMethodMetadata o2) {
				Integer index1 = indexes.get(o1.getMethod());
				Integer index2 = indexes.get(o2.getMethod());
				return index1 - index2;
			}}).sortedCopy(thriftMethods);	
	}
	/**
	 * 返回所有服务接口的原始方法对象{@link Method}
	 * @param metadata
	 * @return
	 */
	public static  List<Method> methodsOf(ThriftServiceMetadata metadata){
		Collection<Method> c = Collections2.transform(
				checkNotNull(metadata,"metadata is null").getMethods().values(), 
				new Function<ThriftMethodMetadata,Method>(){
					@Override
					public Method apply(ThriftMethodMetadata input) {
						return input.getMethod();
					}});
		return Lists.newArrayList(c);
	}
}
